﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
#if WPF
using System.Windows.Controls;
using System.Windows.Shapes;
#else
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Shapes;
using Windows.Foundation;
using Windows.UI.Xaml;
#endif

namespace Eternal.DataVisualization
{
    public abstract class Axis : Control
    {
        public Axis()
        {
            DefaultStyleKey = typeof(Axis);
        }

      

        public double MinZoomCoefficient
        {
            get { return (double)GetValue(MinZoomCoefficientProperty); }
            set { SetValue(MinZoomCoefficientProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MinZoomCoefficient.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MinZoomCoefficientProperty =
            DependencyProperty.Register("MinZoomCoefficient", typeof(double), typeof(Axis), new PropertyMetadata(0.01));

        public bool AllowZoomPan
        {
            get { return (bool)GetValue(AllowZoomPanProperty); }
            set { SetValue(AllowZoomPanProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AllowZoomPan.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AllowZoomPanProperty =
            DependencyProperty.Register("AllowZoomPan", typeof(bool), typeof(Axis), new PropertyMetadata(false));

        public Style GridLineStyle
        {
            get { return (Style)GetValue(GridLineStyleProperty); }
            set { SetValue(GridLineStyleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GridLineStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GridLineStyleProperty =
            DependencyProperty.Register("GridLineStyle", typeof(Style), typeof(Axis), new PropertyMetadata(null));

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(Axis), new PropertyMetadata(null));
        
        public Style AxisLineStyle
        {
            get { return (Style)GetValue(AxisLineStyleProperty); }
            set { SetValue(AxisLineStyleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AxisLineStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AxisLineStyleProperty =
            DependencyProperty.Register("AxisLineStyle", typeof(Style), typeof(Axis), new PropertyMetadata(null));

        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HeaderTemplate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(Axis), new PropertyMetadata(null));

        public IEnumerable AxisData
        {
            get { return (IEnumerable)GetValue(AxisDataProperty); }
            set { SetValue(AxisDataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AxisData.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AxisDataProperty =
            DependencyProperty.Register("AxisData", typeof(IEnumerable), typeof(Axis), new PropertyMetadata(null,OnAxisDataChanged));

        private static void OnAxisDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as Axis).OnDataChanged();
        }

        private void OnDataChanged()
        {
            IterateData();
        }

        public object Interval
        {
            get { return GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Interval.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IntervalProperty =
            DependencyProperty.Register("Interval", typeof(object), typeof(Axis), new PropertyMetadata(null));

        public double OuterTickSize
        {
            get { return (double)GetValue(OuterTickSizeProperty); }
            set { SetValue(OuterTickSizeProperty, value); }
        }

        internal virtual void UpdateMinimumMaximum()
        {
           
            ActualStart = (ActualData as List<double>).Min();
            ActualEnd = (ActualData as List<double>).Max();
           
        }

        private static void OnZoomingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as Axis).OnZoomingChanged();
        }

        internal void OnZoomingChanged()
        {
            if (ZoomCoefficient < 1 || (ZoomOffset > 0 && ZoomCoefficient < 1))
            {
                Start = ActualStart + ZoomOffset * (ActualEnd - ActualStart);
                End = Start + ZoomCoefficient * (ActualEnd - ActualStart);
                if (Start < ActualStart)
                {
                    End = End + (ActualStart - Start);
                    Start = ActualStart;
                }

                if (End > ActualEnd)
                {
                    Start = Start - (End - ActualEnd);
                    End = ActualEnd;
                }
                ActualDelta = Math.Max(End - Start, 1);
            }
            else
            {
                Start = actualStart;
                End = actualEnd;
            }
        }

        public double ZoomCoefficient
        {
            get { return (double)GetValue(ZoomCoefficientProperty); }
            set { SetValue(ZoomCoefficientProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ZoomCoefficient.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ZoomCoefficientProperty =
            DependencyProperty.Register("ZoomCoefficient", typeof(double), typeof(Axis), new PropertyMetadata(1d, OnZoomingChanged));

        public double ZoomOffset
        {
            get { return (double)GetValue(ZoomOffsetProperty); }
            set { SetValue(ZoomOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ZoomOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ZoomOffsetProperty =
            DependencyProperty.Register("ZoomOffset", typeof(double), typeof(Axis), new PropertyMetadata(0d,OnZoomingChanged));



        // Using a DependencyProperty as the backing store for OuterTickSize.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OuterTickSizeProperty =
            DependencyProperty.Register("OuterTickSize", typeof(double), typeof(Axis), new PropertyMetadata(8d));

        public double InnerTickStyle
        {
            get { return (double)GetValue(InnerTickStyleProperty); }
            set { SetValue(InnerTickStyleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InnerTickStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InnerTickStyleProperty =
            DependencyProperty.Register("InnerTickStyle", typeof(double), typeof(Axis), new PropertyMetadata(null));

        public Position AxisPosition
        {
            get { return (Position)GetValue(AxisPositionProperty); }
            set { SetValue(AxisPositionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AxisPosition.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AxisPositionProperty =
            DependencyProperty.Register("AxisPosition", typeof(Position), typeof(Axis), new PropertyMetadata(Position.OutSideArea));

        public Style OuterTickStyle
        {
            get { return (Style)GetValue(OuterTickStyleProperty); }
            set { SetValue(OuterTickStyleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for OuterTickStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OuterTickStyleProperty =
            DependencyProperty.Register("OuterTickStyle", typeof(Style), typeof(Axis), new PropertyMetadata(null));

        protected virtual void IterateData()
        { 
        
        }

        public void DynamicUpdateVisual()
        {
            UpdateVisual();
            //Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(UpdateVisual));
        }

        public void UpdateVisual()
        {
            if (CanUpdate)
            {
                ActualDelta = Math.Max(End - Start, 1);
                CalculateInterval();
                if (padding)
                {
                    ApplyPadding();
                    ActualDelta = Math.Max(End - Start, 1);
                }
                if (AllowZoomPan)
                    OnZoomingChanged();
                GenerateLabelsAndSize();
                GenerateRecycleElement();
                //CanUpdate = false;
            }
        }

        public void ReMeasure()
        {
            if (CanUpdate)
            {
                ActualDelta = Math.Max(End - Start, 1);
                CalculateInterval();
                GenerateLabelsAndSize();
                GenerateRecycleElement();
            }
        }

        protected virtual void AddToVisual()
        {
          
        }

        protected virtual void ApplyPadding()
        {
            padding = false;
        }

        protected virtual void GenerateLabelsAndSize()
        { }

        protected virtual void CalculateInterval()
        { 
        
        }

        public virtual Point GetPoint(Axis axis, double value) 
        {
            return new Point(0, 0);
        }

        protected virtual void GenerateRecycleElement()
        { }

        protected ElementRecycler<ContentPresenter> AxisLabelRecycler { get; set; }

        protected ElementRecycler<Line> TickRecycler { get; set; }

        protected Size tempMeasureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

        protected ContentPresenter contentContrl = new ContentPresenter();

        internal bool CanUpdate { get; set; }

        protected double ActualInterval { get; set; }

        protected List<UIElement> Presenters;

        protected Line AxisLine { get; set; }

        public List<AxisLabel> Labels { get; set; }

        protected ContentPresenter HeaderPresenter { get; set; }

        public Canvas TickPresenter { get; set; }

        public Canvas SmallTickPresenter { get; set; }

        public Canvas LabelPresenter { get; set; }

        public StackPanel RootPanel { get; set; }

        protected bool padding;

        protected Size AvailableSize { get; set; }

        internal Size ActualSize;

        internal string Type { get; set; }

        private IList<double> convertedData;

        public GridLinesPanel GridLinePanel { get; set; }

        public IList<double> ConvertedData
        {
            get { return convertedData; }
            internal set { convertedData = value; }
        }

        private IList actualData;

        public IList ActualData
        {
            get { return actualData; }
            internal set { actualData = value; }
        }

        private double actualDelta;

        public double ActualDelta
        {
            get { return actualDelta; }
            internal set { actualDelta = value; }
        }

        private double actualStart = double.PositiveInfinity;

        public double ActualStart
        {
            get { return actualStart; }
            set
            {
                Start = value;
                actualStart = value;
            }
        }

        private double actualEnd;

        public double Start { get; internal set; }

        public double End { get; internal set; }

        public double ActualEnd
        {
            get { return actualEnd; }
            set
            {
                End = value;
                actualEnd = value;
            }
        }
    }

    public class AxisLabel
    {
        public Point Position { get; set; }
        public FrameworkElement Element { get; set; }
        public double ActualContent { get; set; }
        public object Content { get; set; }
        public bool IsVisible { get; set; }
    }
}
